﻿using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.ComponentModel;
using System.Globalization;

namespace WebSlices
{
    /// <summary>
    /// <see cref="WebSlice"/>s allow users to subscribe to portions of a web page.
    /// </summary>
    [ParseChildren(false), PersistChildren(true)]
    [Designer(typeof(WebSlices.Design.WebSliceDesigner))]
    public class WebSlice : CompositeControl, IWebSlice
    {
        /// <summary>
        /// Gets or sets the amount of time that a client should cache the web slice data.
        /// </summary>
        /// <remarks>
        /// <para>If a <see cref="TimeSpan"/> of 3 hours, the download engine will not check for updates more frequently even if the user has set a 1 hour schedule for all web slices.</para>
        /// </remarks>
        [Category("Behaviour")]
        [DefaultValue(null)]
        public virtual TimeSpan? TimeToLive
        {
            get { return (TimeSpan?)this.ViewState["TimeToLive"]; }
            set { this.ViewState["TimeToLive"] = value; }
        }

        /// <summary>
        /// Gets or sets the ID of the placeholder control that should contain the time to live minutes.
        /// </summary>
        [DefaultValue("timeToLive")]
        [Category("Appearance")]
        public virtual string TimeToLivePlaceholderId
        {
            get { return (this.ViewState["TimeToLivePlaceholderId"] == null) ? "timeToLive" : (string)this.ViewState["TimeToLivePlaceholderId"]; }
            set { this.ViewState["TimeToLivePlaceholderId"] = value; }
        }

        /// <summary>
        /// Gets or sets the date and time when the web slice will expire.
        /// </summary>
        /// <remarks>
        /// <para>Web slices are active until this time is reached, and will likely warn the user before it is about to expire.</para>
        /// </remarks>
        [Category("Behaviour")]
        [DefaultValue(null)]
        public virtual DateTime? EndTime
        {
            get { return (DateTime?)this.ViewState["EndTime"]; }
            set { this.ViewState["EndTime"] = value; }
        }

        /// <summary>
        /// Gets or sets the date time format of the <see cref="EndTime"/> when it is rendered.
        /// </summary>
        [Category("Appearance")]
        [DefaultValue("r")]
        public virtual string EndTimeFormat
        {
            get { return (this.ViewState["EndTimeFormat"] == null) ? "r" : (string)this.ViewState["EndTimeFormat"]; }
            set { this.ViewState["EndTimeFormat"] = value; }
        }

        /// <summary>
        /// Gets or sets the ID of the placeholder control that should contain the end time date.
        /// </summary>
        [DefaultValue("endTime")]
        [Category("Appearance")]
        public virtual string EndTimePlaceholderId
        {
            get { return (this.ViewState["EndTimePlaceholderId"] == null) ? "endTime" : (string)this.ViewState["EndTimePlaceholderId"];}
            set { this.ViewState["EndTimePlaceholderId"] = value; }
        }

        /// <summary>
        /// Gets or sets the web slice title.
        /// </summary>
        [Category("Appearance")]
        public virtual string Title
        {
            get { return (string)this.ViewState["Title"]; }
            set { this.ViewState["Title"] = value; }
        }

        /// <summary>
        /// Gets the <see cref="T:System.Web.UI.HtmlTextWriterTag"/> value that corresponds to this Web server control. This property is used primarily by control developers.
        /// </summary>
        /// <returns>One of the <see cref="T:System.Web.UI.HtmlTextWriterTag"/> enumeration values.</returns>
        protected override HtmlTextWriterTag TagKey
        {
            get { return HtmlTextWriterTag.Div; }
        }

        /// <summary>
        /// Gets the <see cref="T:System.Web.UI.HtmlTextWriterTag"/> value that corresponds to the web slice's <see cref="Title"/>.
        /// </summary>
        /// <returns>One of the <see cref="T:System.Web.UI.HtmlTextWriterTag"/> enumeration values.</returns>
        /// <remarks>
        /// <para>The default tag is <see cref="HtmlTextWriterTag.H4"/>.</para>
        /// </remarks>
        protected virtual HtmlTextWriterTag TitleTagKey
        {
            get { return HtmlTextWriterTag.H4; }
        }

        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            this.PrepareContainerControls();
        }

        /// <summary>
        /// Prepares the <see cref="EndTime"/> and <see cref="TimeToLive"/> container controls if required.
        /// </summary>
        protected virtual void PrepareContainerControls()
        {
            if (this.EndTime.HasValue)
            {
                Control endTimeContainer = this.FindControl(this.EndTimePlaceholderId);

                if (endTimeContainer == null)
                {
                    throw new HttpException("When EndTime is specified a container control with the ID matching EndTimePlaceholderId must exist in the WebSlice's control tree.");
                }

                HtmlGenericControl endTimeAbbr = new HtmlGenericControl("abbr");
                endTimeAbbr.Attributes.Add("class", "end-time");
                endTimeAbbr.Attributes.Add("title", this.EndTime.Value.ToString("s"));
                endTimeAbbr.InnerText = HttpUtility.HtmlEncode(this.EndTime.Value.ToString(this.EndTimeFormat));
                endTimeContainer.Controls.Add(endTimeAbbr);
            }

            if (this.TimeToLive.HasValue)
            {
                Control timeToLiveContainer = this.FindControl(this.TimeToLivePlaceholderId);

                if (timeToLiveContainer == null)
                {
                    throw new HttpException("When TimeToLive is specified a container control with the ID matching TimeToLivePlaceholderId must exist in the WebSlice's control tree.");
                }

                Label ttlLabel = new Label();
                ttlLabel.Text = ((int)this.TimeToLive.Value.TotalMinutes).ToString();
                ttlLabel.CssClass = "ttl";
                timeToLiveContainer.Controls.Add(ttlLabel);
            }
        }

        /// <summary>
        /// Adds HTML attributes and styles that need to be rendered to the specified <see cref="T:System.Web.UI.HtmlTextWriterTag"/>. This method is used primarily by control developers.
        /// </summary>
        /// <param name="writer">A <see cref="T:System.Web.UI.HtmlTextWriter"/> that represents the output stream to render HTML content on the client.</param>
        protected override void AddAttributesToRender(HtmlTextWriter writer)
        {
            this.EnsureID();

            if (string.IsNullOrEmpty(this.CssClass))
            {
                this.CssClass = "hslice";
            }
            else
            {
                this.CssClass = string.Concat(this.CssClass, " hslice");
            }

            base.AddAttributesToRender(writer);
        }

        /// <summary>
        /// Renders the contents of the control to the specified writer. This method is used primarily by control developers.
        /// </summary>
        /// <param name="writer">A <see cref="T:System.Web.UI.HtmlTextWriter"/> that represents the output stream to render HTML content on the client.</param>
        protected override void RenderContents(HtmlTextWriter writer)
        {
            if (string.IsNullOrEmpty(this.Title))
            {
                throw new HttpException("WebSlice controls must have a Title specified.");
            }

            this.RenderSliceTitle(writer);
            this.RenderSliceContents(writer);
        }

        /// <summary>
        /// Renders the web slice's title to the specified writer.
        /// </summary>
        /// <param name="writer">A <see cref="T:System.Web.UI.HtmlTextWriter"/> that represents the output stream to render HTML content on the client.</param>
        protected virtual void RenderSliceTitle(HtmlTextWriter writer)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Class, "entry-title");
            writer.RenderBeginTag(this.TitleTagKey);
            writer.WriteEncodedText(this.Title);
            writer.RenderEndTag();
        }

        /// <summary>
        /// Renders the web slice's contents to the specified writer.
        /// </summary>
        /// <param name="writer">A <see cref="T:System.Web.UI.HtmlTextWriter"/> that represents the output stream to render HTML content on the client.</param>
        protected virtual void RenderSliceContents(HtmlTextWriter writer)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Class, "entry-content");
            writer.RenderBeginTag(HtmlTextWriterTag.Div);
            base.RenderChildren(writer);
            writer.RenderEndTag();
        }

        /// <summary>
        /// Gets the collection of web slice content controls.
        /// </summary>
        ControlCollection IWebSlice.Content
        {
            get { return this.Controls; }
        }

        /// <summary>
        /// Gets or sets the client ID of the web slice.
        /// </summary>
        string IWebSlice.Id
        {
            get { return this.ClientID; }
            set { }
        }
    }
}